package com.blog.dao.base.impl;

import com.blog.beans.PageBean;
import com.blog.dao.base.Dao;
import com.blog.util.PageUtil;
import com.blog.util.ReflectionUtil;
import org.hibernate.*;
import org.hibernate.criterion.DetachedCriteria;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 * Author:  Wu Yujie
 * Email:  coffee377@dingtalk.com
 * Time:  2017/02/18 16:41
 */
@Repository
public class DaoImpl<E> implements Dao<E> {
    private Class<E> clazz;

    @Resource
    private SessionFactory sessionFactory;

    private Session session;

    public DaoImpl() {
        clazz = ReflectionUtil.getSuperGenericType(getClass());
    }

    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public Session getSession() {
        return session;
    }

    public void setSession(Session session) {
        this.session = session;
    }

    protected void checkSessionState() {
        session = sessionFactory.getCurrentSession();
    }

    @Override
    public Serializable save(E entity) {
        this.checkSessionState();
        try {
            return session.save(entity);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return -1;
    }

    @Override
    public void persist(E entity) {
        this.checkSessionState();
        try {
            session.persist(entity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void delete(E entity) {
        this.checkSessionState();
        try {
            session.delete(entity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    @Override
    public void deleteAll(Collection<?> entities) {
        this.checkSessionState();
        try {
            for (Object entity : entities) {
                session.delete(entity);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void update(E entity) {
        this.checkSessionState();
        try {
            session.update(entity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public E merge(E entity) {
        this.checkSessionState();
        try {
            return (E) session.merge(entity);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return entity;
    }

    @Override
    @SuppressWarnings("unchecked")
    public E get(Serializable id) {
        this.checkSessionState();
        E entity = null;
        try {
            entity = (E) session.get(clazz, id);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return entity;
    }

    @Override
    @SuppressWarnings("unchecked")
    public E get(Serializable id, LockOptions lockOptions) {
        if (session == null) {
            session = sessionFactory.openSession();
        }
        E entity = null;
        try {
            entity = (E) session.get(clazz, id, lockOptions);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return entity;
    }

    @Override
    @SuppressWarnings("unchecked")
    public E load(Serializable id) {
        this.checkSessionState();
        return (E) session.load(clazz, id);
    }

    @Override
    @SuppressWarnings("unchecked")
    public E load(Serializable id, LockOptions lockOptions) {
        this.checkSessionState();
        return (E) session.load(clazz, id, lockOptions);

    }

    @Override
    public boolean contains(E entity) {
        this.checkSessionState();
        return session.contains(entity);
    }

    @Override
    public List<E> find(String hql, Object... params) {
        return find(-1, -1, hql, params);
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<E> find(int pageNumber, int pageSize, String hql, Object... params) {
        List<E> list = new ArrayList<>();
        this.checkSessionState();
        try {
            Query query = session.createQuery(hql);
            if (params != null && params.length > 0) {
                for (int i = 0; i < params.length; i++) {
                    query.setParameter(i, params[i]);
                }
            }
            if (pageNumber > 0 && pageSize > 0) {
                query.setFirstResult((pageNumber - 1) * pageSize);
                query.setMaxResults(pageSize);
            }
            if (query.list() != null && query.list().size() != 0) {
                list = (List<E>) query.list();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<E> findByCriteria(int pageNumber, int pageSize, Criteria criteria) {
        List<E> list = new ArrayList<>();
        try {
            if (pageNumber > 0 && pageSize > 0) {
                criteria.setFirstResult((pageNumber - 1) * pageSize);
                criteria.setMaxResults(pageSize);
            }
            //criteria.list() 没有数据返回空的数组，即size=0,并不是null
            if (criteria != null && criteria.list().size() != 0) {
                list = (List<E>) criteria.list();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }

    @Override
    public List<E> findByCriteria(Criteria criteria) {
        return findByCriteria(-1, -1, criteria);
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<E> findByDetachedCriteria(int pageNumber, int pageSize, DetachedCriteria detachedCriteria) {
        List<E> list = new ArrayList<>();
        this.checkSessionState();
        try {
            Criteria criteria = detachedCriteria.getExecutableCriteria(session);
            if (pageNumber > 0 && pageSize > 0) {
                criteria.setFirstResult((pageNumber - 1) * pageSize);
                criteria.setMaxResults(pageSize);
            }
            if (criteria != null && criteria.list().size() != 0) {
                list = (List<E>) criteria.list();
            }
        } catch (HibernateException e) {
            e.printStackTrace();
        }
        return list;
    }

    @Override
    public List<E> findByDetachedCriteria(DetachedCriteria detachedCriteria) {
        return findByDetachedCriteria(-1, -1, detachedCriteria);
    }

    @Override
    public Integer getCount(String hql, Object... params) {
        return find(hql, params).size();
    }

    @Override
    public Integer getCount(Criteria criteria) {
        return findByCriteria(criteria).size();
    }

    @Override
    public Integer getCount(DetachedCriteria detachedCriteria) {
        return findByDetachedCriteria(detachedCriteria).size();
    }

    @Override
    public PageBean<E> findForPage(int pageNumber, int pageSize, String hql, Object... params) {
        // TODO: 2016/12/28 18:07 统计数量必须强前置，否则不是预期结果，初步断定是 Hibernate 缓存的原因
        int totalSize = getCount(hql, params);
        List<E> entityList = find(pageNumber, pageSize, hql, params);
        int totalPage = PageUtil.countTotalPage(totalSize, pageSize);
        return new PageBean<>(entityList, totalSize, totalPage, pageNumber, pageSize);
    }

    @Override
    public PageBean<E> findForPage(int pageNumber, int pageSize, Criteria criteria) {
        // TODO: 2016/12/28 18:07 统计数量必须强前置，否则不是预期结果，初步断定是 Hibernate 缓存的原因
        int totalSize = getCount(criteria);
        List<E> entityList = findByCriteria(pageNumber, pageSize, criteria);
        int totalPage = PageUtil.countTotalPage(totalSize, pageSize);
        return new PageBean<>(entityList, totalSize, totalPage, pageNumber, pageSize);
    }

    @Override
    public PageBean<E> findForPage(int pageNumber, int pageSize, DetachedCriteria detachedCriteria) {
        // TODO: 2016/12/28 18:07 统计数量必须强前置，否则不是预期结果，初步断定是 Hibernate 缓存的原因
        int totalSize = getCount(detachedCriteria);
        List<E> entityList = findByDetachedCriteria(pageNumber, pageSize, detachedCriteria);
        int totalPage = PageUtil.countTotalPage(totalSize, pageSize);
        return new PageBean<>(entityList, totalSize, totalPage, pageNumber, pageSize);
    }
}
